import { createReadStream, createWriteStream, writeFileSync } from "node:fs";
import { posix } from "node:path";
import { Context, Controller, Post, sfn } from "nb2";
import { ResBody } from "../common/model.js";
import { generateRandom } from "../common/utils.js";

/**
 * 文件上传 ｜ 下载
 * 支持格式：FormData | ArrayBuffer
 */
@Controller("files")
export class Files {
  @Post("download")
  [sfn()](ctx: Context) {
    const file_name = "c.png";
    const file_path = posix.join(process.env.STATIC_PATH!, file_name);
    ctx.status(200);
    ctx.res.setHeader("Content-Type", ctx.getContentType("octet-stream"));
    ctx.res.setHeader(
      "Content-Disposition",
      `attachment; filename=${file_name}`
    );
    createReadStream(file_path).pipe(ctx.res);
  }

  /**限制最大上传，单位MB*/
  __maxSize = 10;
  __uploadPath = posix.join(process.env.STATIC_PATH!, process.env.UPLOAD_DIR!);

  /**
   * 创建新的文件名
   * @param name
   */
  createFileName(name: string) {
    const ext = posix.extname(name);
    const base = name.split(ext)[0];
    return base + "." + generateRandom(6) + ext;
  }

  @Post("upfile-stream")
  async [sfn()](ctx: Context) {
    const contenttype = ctx.req.headers["content-type"] || "";
    if (!contenttype.includes("application/octet-stream")) {
      return ctx.json<ResBody>({
        code: 400,
        msg: `content-type必须是application/octet-stream`,
      });
    }
    const length = ctx.req.headers["content-length"] || 0;
    if (this.__maxSize * 1024 * 1024 < Number(length)) {
      return ctx.json<ResBody>({
        code: 400,
        result: `超出最大上传尺寸${this.__maxSize}mb`,
      });
    }

    let filename: string;
    const disposition = ctx.req.headers["content-disposition"] || "";
    if (disposition && disposition.includes("filename")) {
      filename = disposition.match(/filename=([^;]+)/)?.[1] || "";
      filename = filename.slice(1, -1);
    } else {
      //生成一个没有后缀的文件名
      filename = "file_" + generateRandom(10);
    }

    const writeStream = createWriteStream(
      posix.join(this.__uploadPath, filename)
    );
    ctx.req.pipe(writeStream);
    ctx.req.on("end", () => {
      const url = posix.join(ctx.app.cname, process.env.UPLOAD_DIR!, filename);
      return ctx.json<ResBody>({ code: 200, msg: "上传成功", url });
    });

    writeStream.on("error", (err) => {
      console.error("Error writing to file:", err);
      return ctx.json<ResBody>({ code: 400, result: "上传失败" });
    });
  }

  @Post("upfile-formdata")
  async [sfn()](ctx: Context) {
    const [contentLength, contentType] = [
      parseInt(ctx.req.headers["content-length"] ?? ""),
      ctx.req.headers["content-type"],
    ];

    const boundary = contentType?.match(/boundary=([^;]+)/)?.[1];

    //NaN | 0 排除
    if (!contentLength) {
      return ctx.json<ResBody>({
        code: 400,
        result: `content-length错误，contentLength：${contentLength}`,
      });
    }

    //超尺寸限制限制
    if (contentLength > this.__maxSize * 1024 * 1024) {
      return ctx.json<ResBody>({
        code: 400,
        result: `超出最大上传尺寸${this.__maxSize}mb`,
      });
    }

    // 解析数据
    if (contentType?.includes("multipart/form-data") && boundary) {
      /**
       * 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryuA6k9Vw0kI6GjOjd'
       * 前端不用设置content-type，会自动设置，手动设置，需要指定boundary
       */

      //接收数据
      const body = await ctx.body("buffer");
      const ret = ctx.parseFormData(body, boundary, contentLength);

      //写入文件
      ret.forEach((d) => {
        if (d.filename) {
          writeFileSync(
            posix.join(this.__uploadPath, this.createFileName(d.filename)),
            d.data
          );
        }
      });

      const ret2 = ret.map((d) => {
        let newFileName: string | undefined, url: string | undefined;

        if (d.filename) {
          newFileName = this.createFileName(d.filename);
          //实现返回文件，的访问url地址
          url = posix.join(ctx.app.cname, process.env.UPLOAD_DIR!, newFileName);
          //写入文件
          writeFileSync(posix.join(this.__uploadPath, newFileName), d.data);
        }

        return {
          type: d.ContentType,
          filename: d.filename,
          newFileName,
          url,
        };
      });
      return ctx.json<ResBody>({ code: 200, result: ret2 });
    }

    ctx.json<ResBody>({
      code: 200,
      result: `不支持的content-type: ${contentType}`,
    });
  }
}

/*
  下载 前端代码
  let filename;
  const blob = await fetch('/api/files/download', { method: 'POST' })
    .then(res => {
      const cd = res.headers.get('Content-Disposition')
      let [_, _filename] = cd.match(/filename=([^;]+)/) ?? []
      filename = _filename
      return res.blob()
    })
  // console.log(ret, m)

  // 创建下载链接
  const href = window.URL.createObjectURL(blob)
  // 创建a标签并为其添加属性
  const link = document.createElement('a')
  link.href = href
  link.download = filename
  // 触发事件
  link.click()
*/
